from typing import NamedTuple, Dict, List
from collections import namedtuple
import time

from pytube import YouTube
from pytube.streams import Stream
from http.client import IncompleteRead

import message as message_module


class Video:
    """ Класс связанный с загрузкой видео """

    def __init__(self):
        self.message_terminal_object = message_module.MessageTerminal()

    def get_video_object(self, link_to_video):
        """ Получаем объект видео """

        yt = YouTube(link_to_video.strip())
        return yt

    def convert_list_of_possible_streams_to_dict(self,
                                                 list_of_streams: NamedTuple,
                                                 is_all_downloads: bool) -> List[Stream]:
        """ Преобразуем список возможных загруженных потоков в словарь """

        def _get_required_options(stream: Stream, required_options: List[str]) -> str:
            """ Получаем необходимые параметры объекта stream"""

            options = ' '.join(
                [str(getattr(stream, options)) for options in required_options]
            )

            if 'fps' in required_options: options += 'fps'
            if is_all_downloads: options += ' [best]'

            return options

        def _add_other_streams(streams: List[Stream],
                               StreamObject: NamedTuple,
                               list_of_possible_loaded_streams: Dict[int, Stream],
                               count: int) -> None:
            """ Добвляем остальные потоки """

            for stream in streams:

                if type_of_stream == 'video':
                    StreamObject = namedtuple('Video', ['video', 'options'])
                    options = f'Video {stream.mime_type} {stream.resolution} {stream.fps}fps'
                elif type_of_stream == 'audio':
                    StreamObject = namedtuple('Audio', ['audio', 'options'])
                    options = f'Audio {stream.mime_type} {stream.abr}'
                else:
                    raise ValueError()

                list_of_possible_loaded_streams[count] = StreamObject(stream, options)
                count += 1


        # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

        count = 0
        list_of_possible_loaded_streams = {}

        for type_of_stream in ['video', 'audio']:

            if type_of_stream == 'video':
                StreamObject = namedtuple('Video', ['video', 'options'])
                best_stream = list_of_streams.best_video_quality
                options = 'Video ' + _get_required_options(
                    list_of_streams.best_video_quality,
                    ['mime_type', 'resolution', 'fps']
                )
                streams = list_of_streams.list_of_streams_type_video
            elif type_of_stream == 'audio':
                StreamObject = namedtuple('Audio', ['audio', 'options'])
                best_stream = list_of_streams.best_audio_quality
                options = 'Audio ' + _get_required_options(
                    list_of_streams.best_video_quality,
                    ['mime_type', 'abr']
                )
                streams = list_of_streams.list_of_streams_type_audio
            else:
                raise ValueError()

            list_of_possible_loaded_streams[count] = StreamObject(
                best_stream,
                options
            )

            count += 1

            if is_all_downloads:
                _add_other_streams(
                    streams, StreamObject,
                    list_of_possible_loaded_streams, count
                )

        return list_of_possible_loaded_streams


    def get_list_of_possible_streams(self,
                                     yt_object: YouTube,
                                     is_all_downloads: bool,
                                     convert_to_dict: bool = False) -> NamedTuple:
        """ Получаем список возможных загруженных потоков """

        ListOfPossibleStreams = namedtuple('ListOfPossibleStreams', [
            'list_of_streams_type_video', 'best_video_quality',
            'list_of_streams_type_audio', 'best_audio_quality',
        ])

        list_of_streams_type_video = yt_object.streams.filter(type='video').asc()
        best_video_quality = list_of_streams_type_video.get_highest_resolution()

        list_of_streams_type_audio = yt_object.streams.filter(type='audio').asc()
        best_audio_quality = list_of_streams_type_audio.get_highest_resolution()

        list_of_possible_streams = ListOfPossibleStreams(
            list_of_streams_type_video, best_video_quality,
            list_of_streams_type_audio, best_audio_quality,
        )

        if convert_to_dict:
            return self.convert_list_of_possible_streams_to_dict(
                list_of_possible_streams,
                is_all_downloads
            )
        else:
            return list_of_possible_streams


    def get_basic_information_of_video(self,
                                       yt_object: YouTube,
                                       format: bool = False) -> Dict:
        """ Получаем основную информацию о видео """

        basic_information = {}
        required_parameters = [
            'author', 'description', 'keywords',
            'length', 'metadata', 'publish_date',
            'title', 'views', 'rating'
        ]
        for parameter in required_parameters:
            try:
                basic_information[parameter] = getattr(yt_object, parameter)
            except IncompleteRead:
                print('Opps...')
                self.get_basic_information_of_video(
                    yt_object,
                    format
                )

        if format:
            return self.format_basic_video_information(basic_information)
        else:
            return basic_information


    def format_basic_video_information(self, basic_information: Dict) -> Dict:
        """ Приводим информацию о видео в нужный вид """

        # Количество просмотров
        views = '{0:,}'.format(
            basic_information['views']
        ).replace(',', ' ')

        # Дата публикации
        publish_date = basic_information['publish_date'].strftime(
            '%H:%M:%S %d-%m-%Y'
        )

        # Продолжительность видео
        length = time.gmtime(basic_information['length'])
        length = time.strftime('%H:%M:%S', length)

        basic_information['views'] = views
        basic_information['publish_date'] = publish_date
        basic_information['length'] = length
        basic_information['keywords'] = ', '.join(basic_information['keywords'])

        return basic_information


    def download_selected_content(self, content: Stream) -> None:
        """ Устанавливаем какой-то контент """

        if 'video' in dir(content):
            content = content.video
            content_type = 'video'
        elif 'audio' in dir(content):
            content = content.audio
            content_type = 'audio'
        else:
            raise ValueError(f'Переданный аргумент не обрабатывается: {content}')

        content.download()
        self.message_terminal_object.show_message_about_downloading_content(
            content_type
        )
